
#include <malloc.h>

#include "ordenamiento-adicional.h"
#include "../varios/log.h"
#include "../varios/mensajes.h"
#include "../varios/compresion.h"
#include "../varios/mpi-utiles.h"
#include "../../compartido/vectores.h"


// Definición de funciones propias de este archivo
struct VECTOR *juntarVectorLocalConRemoto(int origenVectorRemoto, long long tamVectorRemoto, struct VECTOR *vectorLocal);


/*
 * Implementacion de funcion definida en cabecera
 */
struct VECTOR *ordenarVectoresRemotos(struct VECTOR *vectorLocal, int cantVectInterm, int *originesInterm, long long *tamVectInterm) {
    
    struct VECTOR *vectorActual = vectorLocal; // Aca guardamos el resultado de cada iteracion
    
    int i; // Procesamos cada vector intermedio secuencialmente
    for (i=0; i<cantVectInterm; i++) {
        struct VECTOR *vectTemp = juntarVectorLocalConRemoto(originesInterm[i], tamVectInterm[i], vectorActual);
        eliminarVector(vectorActual);
        vectorActual = vectTemp;
    }
    
    // Devolvemos el vector resultante
    return vectorActual;

}


struct VECTOR *juntarVectorLocalConRemoto(int origenVectRem, long long tamVectRem, struct VECTOR *vectLocal) {
    
    long long tamVectLocal = obtenerDimension(vectLocal);

    // Creamos el vector resultante
    long long tamTotal = tamVectLocal + tamVectRem;
    struct VECTOR *vectorResultante = crearVector(tamTotal);
    
    // Creamos el buffer del vector que recibiremos
    int *bufferRemoto = NULL;
        
    // Indice que apuntará al próximo posición a escribir en el vector resultante
    long long indiceResultante = 0;
    
    // Indican el proximo elemento a comparar del vector remoto
    long long offsetBufferVectRem = 0;
    
    // Indican el offset total de los vectores
    long long offsetTotalVectLocal = 0;
    long long offsetTotalVectRem = 0;
    
    // Ultimo elemento cargado del vector remoto
    long long ultimoElemCargVectRem = -1;
    
    // Cuando se hayan leido todos los elementos remotos y se ordenen, se saldra del bucle
    while (indiceResultante<tamTotal) {
        
        LOGGER("Inicio iteracion. Leidos al momento %l de %l", indiceResultante, tamTotal);

        // Esperamos a que lleguen mas elementos del vector remoto si es necesario
        if (offsetTotalVectRem>ultimoElemCargVectRem && offsetTotalVectRem<tamVectRem) {
            
            LOGGER("Cargamos elementos del vector remoto. Ultimo elemento cargado: %l de %l", ultimoElemCargVectRem, tamVectRem);
            
            if (bufferRemoto != NULL) {
                free(bufferRemoto);
            }
            
            struct DATOS_RECIBIDOS recepcion = recibirDatos(origenVectRem, MSG_VECTOR_FINAL);
            struct DATOS_DESCOMPRIMIDOS descomp = descomprimir(recepcion.datos);
            free(recepcion.datos);
            bufferRemoto = (int *)descomp.datos;
            
            offsetBufferVectRem = 0;
            ultimoElemCargVectRem += descomp.longitud/sizeof(int);
            
            LOGGER("Obtuvimos %l elementos para el vector remoto", descomp.longitud/sizeof(int));
            
        }
        
        if (offsetTotalVectLocal==tamVectLocal) {
            escribirValor(vectorResultante, bufferRemoto[offsetBufferVectRem], indiceResultante);
            offsetBufferVectRem++;
            offsetTotalVectRem++;
        } else if (offsetTotalVectRem==tamVectRem) { // Lo mismo para el vector local
            int valor = obtenerValor(vectLocal, offsetTotalVectLocal);
            escribirValor(vectorResultante, valor, indiceResultante);
            offsetTotalVectLocal++;
        } else {
            int valorVectLocal = obtenerValor(vectLocal, offsetTotalVectLocal);
            if (valorVectLocal <= bufferRemoto[offsetBufferVectRem]) {
                escribirValor(vectorResultante, valorVectLocal, indiceResultante);
                offsetTotalVectLocal++;
            } else {
                escribirValor(vectorResultante, bufferRemoto[offsetBufferVectRem], indiceResultante);
                offsetBufferVectRem++;
                offsetTotalVectRem++;
            }
            
        }
        
        indiceResultante++;
        
        LOGGER("Fin iteracion exterior");

    }

    LOGGER("Fin de ordenamiento adicional remoto");
    
    // Liberamos memoria
    free(bufferRemoto);
    
    return vectorResultante;
    
}


